﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Linq;
using System.Text;
using XsqTech.SmartOA.Infrastructure.JWT;

namespace XsqTech.SmartOA.Application.ServiceExtensions
{
    public static class JWTSetupExt
    {
        public static void AddJWTSetup(this IServiceCollection services,JwtTokenOption jwtTokenConfig)
        {

            services.AddSwaggerGen(c =>
            {
                var basePath = AppContext.BaseDirectory; //获取应用程序的所在目录
                                                         //或者用下面的方式也能获取
                var basePath2 = Path.GetDirectoryName(typeof(Program).Assembly.Location);
                var xmlPath = System.IO.Path.Combine(basePath, "XsqTech.SmartOA.Application.xml"); //拼接XML文件所在路径
                                                                               //让Swagger显示方法、类的XML注释信息
                c.IncludeXmlComments(xmlPath, true);
                //设置Swagger文档参数
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "XsqTech.SmartOA.Application.xml",
                    Version = "v1",
                    Description = "Asp.Net Core6 WebApi开发实战", //描述信息
                    Contact = new OpenApiContact() //开发者信息
                    {
                        Name = "雨宫科技", //开发者姓名
                        Email = "2357035391@qq.com", //email地址
                        Url = new Uri("https://gitee.com/xuerenwang") //作者的主页网站
                    }
                });
                //开启Authorize权限按钮
                c.AddSecurityDefinition("JWTBearer", new OpenApiSecurityScheme()
                {
                    Description = "这是方式一(直接在输入框中输入认证信息，不需要在开头添加Bearer) ",
                    Name = "Authorization", //jwt默认的参数名称
                    In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中)
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer"
                });
                //定义JwtBearer认证方式二
                //options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
                //{
                // Description = "这是方式二(JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer { token}（注意两者之间是一个空格）)",
                // Name = "Authorization",//jwt默认的参数名称
                // In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                // Type = SecuritySchemeType.ApiKey
                //});
                //声明一个Scheme，注意下面的Id要和上面AddSecurityDefinition中的参数name一致
                var scheme = new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference()
                    {
                        Id = "JWTBearer", //这个名字与上面的一样
                        Type = ReferenceType.SecurityScheme
                    }
                };
                //注册全局认证（所有的接口都可以使用认证）
                c.AddSecurityRequirement(new OpenApiSecurityRequirement{{ scheme, Array.Empty<string>() }});
            });

            //配置JwtBearer身份认证服务
            services.AddAuthentication(opts =>
            {
                opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; //认证模式
                opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; //质询模式
            })
            .AddJwtBearer( //对JwtBearer进行配置
            x =>
            {
                x.RequireHttpsMetadata = true; //设置元数据地址或权限是否需要HTTP
                x.SaveToken = true;
                //Token验证参数
                x.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true, //是否验证Issuer
                    ValidIssuer = jwtTokenConfig.Issuer,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenConfig.IssuerSigningKey)),
                    ValidateAudience = true,
                    ValidAudience = jwtTokenConfig.Audience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromMinutes(1) //对token过期时间验证的允许时间
                };
                //如果jwt过期，在返回的header中加入Token-Expired字段为true，前端在获取返回header时判断
                x.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = context =>
                    {
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        return Task.CompletedTask;
                    }
                };
            }
            );
        }
    }
}
